
mod gat;
mod src_channel_sink;

use std::fs::File;
use std::io::{SeekFrom, Read, Seek};

struct Tail {
    fname: String,
    lines: u64,
}
impl Tail {
    fn new(fname: &str, lines: u64) -> Self {
        Self {
            fname : fname.to_owned(), 
            lines,
        }
    }


    fn get_page_size() -> u64 {
        //1024_u64
        8*1024_u64
    }

    fn get_file_size(fname: &str) -> u64 {
        let metadata = std::fs::metadata(fname);
        match metadata {
            Ok(data) => data.len(),
            Err(_) => 0,
        }
    }

    fn print(&self) -> std::io::Result<()>{
        if self.lines == 0 {
            return Ok(());
        }

        let mut reader = File::open(&self.fname)?;

        let page_size = Self::get_page_size();
        let mut buffer = vec![0_u8; page_size as usize];

        let mut end = Self::get_file_size(&self.fname);

        let mut lines = self.lines;

        println!("file size is :{end}, lines:{lines}");
        while end > 0 {
            let start =  if end > page_size {
                end - page_size
            }
            else { // end <= page_size
                0
            };
            let len = (end - start) as usize;
            println!("hello : start:{start}, end:{end}, len:{len}");

            let seek = reader.seek(SeekFrom::Start(start))?;
            let readed = reader.read(&mut buffer)?;
            println!("seek:{seek}, readed:{readed:?}");
            //println!("data:{:?}", &buffer[..len]);


            //search the lines 
            if lines > 0 {
                let mut break_i = None;
                for i in 0..len {
                    if buffer[len-i-1]  == 0x0a {
                        lines -= 1;   
                        println!("lines: {lines}, iiii:{i}, val:{}", len - i - 1);
                        if lines == 0 {  
                            break_i = Some(len-i-1);
                            println!("iiiii: {i}");
                            break;
                        }
                    }
                };
                
            
                if let Some(i) = break_i {
                    self.dump_remaining(i, len, &mut buffer[..]);
                    break;
                }
            }
            else{
                println!("lines:{lines} end");
                break;
            }
        
            end = start 
        }

        Ok(())
    }

    fn dump_remaining(&self, i: usize, len: usize, buffer: &mut [u8]){
        //TODO: print remaining of all file content
        println!("break_iiiii: {i}");
        println!("{}", &String::from_utf8_lossy(&buffer[i..len]));
    }
}


#[cfg(test)]
mod test {
    //use super::*;

    #[test]
    fn test() -> std::io::Result<()>{
        let val = Tail::new("../src/main.rs", 100).print();
        match val {
            Err(e) => println!("{e}"),
            Ok(_) => println!("print ok"),
        }
    }
        
}


/*
    let src = Source::new("file.txt");
    let sink = Sink::from_console("Console");
    let channel = Channel::new()

    channel.sink(sink)
    src.channel(channel)
    let agent = Agent::new().src(src)


    let channel1 = Channel::new().add_sink(sink1)
    let channel2 = Channel::new().add_sink(sink2)
    let channel3 = Channel::new().add_sink(sink3)

    //src.add_channel(channel1).add_channel(channel2).add_channel(channel3)
    src.add_channels([channel1, channel2, channel3])

    let agent = Agent::new().source(src).sources(src[1, 2, 3])



    channel.sink(sink1).sink(sink2).sink(sink3);
    src.channel(channel)

    let agent = Agent::new().src(src);

    agent.run()



    let processor = Processor::one_to_one().src(src).sink(sink);
    let processor = Processor::one_to_many().src(src).sink(sink[..]);
    let processor = Processor::many_to_one().src(src[..]).sink(sink);
    let processor = Processor::many_to_man().pair(src[..], sink).pair(src, sink[..]).pair(src, sink);
    
    processor.run()
*/
